#include "swoole.h"
#include <sys/signalfd.h>

static int swSignalfd_onSignal(swReactor *reactor, swEvent *event);

static int signal_fd = 0;
static sigset_t signalfd_mask;
static int _lock = 0;
typedef struct
{
	swSignalHander callback;
	uint16_t signo;
	uint16_t active;
} swSignal;

static swSignal signals[SW_SIGNO_MAX];

void swSignalfd_init(){
	sigemptyset(&signalfd_mask);
	bzero(&signals,sizeof(signals));
}

int swSignalfd_setup(swReactor *reactor){
	if(signal_fd == 0){
		signal_fd = signalfd(-1,&signalfd_mask,SFD_NONBLOCK | SFD_CLOEXEC);
		if(signal_fd < 0){
			return -1;
		}

		SwooleG.signal_fd = signal_fd;

		if(sigprocmask(SIG_BLOCK,&signalfd_mask,NULL) == -1){
			return -1;
		}

		reactor->setHandle(reactor, SW_FD_SIGNAL, swSignalfd_onSignal);
        reactor->add(reactor, signal_fd, SW_FD_SIGNAL);

		return 0;
	}

	return -1;
}

static int swSignalfd_onSignal(swReactor *reactor, swEvent *event){
	int n;
	struct signalfd_siginfo siginfo;
	n = read(event->fd,&siginfo,sizeof(siginfo));
	if(n < 0){
		return 0;
	}

	if(signals[siginfo.ssi_signo].active && signals[siginfo.ssi_signo].callback){
		signals[siginfo.ssi_signo].callback(siginfo.ssi_signo);
	}

	return 0;
}

static void swSignalfd_set(int signo, swSignalHander callback){
	//clear 
	if(callback == NULL && signals[signo].active){
		sigdelset(&signalfd_mask,signo);
		bzero(&signals[signo],sizeof(swSignal));
	}else{
		sigaddset(&signalfd_mask, signo);
        signals[signo].callback = callback;
        signals[signo].signo = signo;
        signals[signo].active = 1;
	}

	if(signal_fd > 0){
		sigprocmask(SIG_BLOCK, &signalfd_mask, NULL);
        signalfd(signal_fd, &signalfd_mask, SFD_NONBLOCK | SFD_CLOEXEC);
	}
}

swSignalHander swSignal_set(int sig, swSignalHander func, int restart, int mask)
{
    //ignore
    if (func == NULL)
    {
        func = SIG_IGN;
    }
    //clear
    else if ((long) func == -1)
    {
        func = SIG_DFL;
    }

    struct sigaction act, oact;
    act.sa_handler = func;
    if (mask)
    {
        sigfillset(&act.sa_mask);
    }
    else
    {
        sigemptyset(&act.sa_mask);
    }
    act.sa_flags = 0;
    if (sigaction(sig, &act, &oact) < 0)
    {
        return NULL;
    }
    return oact.sa_handler;
}

void swSignal_callback(int signo)
{
    if (signo >= SW_SIGNO_MAX)
    {
        printf("signal[%d] numberis invalid.", signo);
        return;
    }
    swSignalHander callback = signals[signo].callback;
    if (!callback)
    {
        printf("signal[%d] callback is null.", signo);
        return;
    }
    callback(signo);
}

static void swSignal_async_handler(int signo){
	_lock = 1;
	swSignal_callback(signo);
	_lock = 0;
}

void swSignal_add(int signo, swSignalHander func){
	if(SwooleG.use_signalfd){
		swSignalfd_set(signo, func);
	}else{
		//manager process
		signals[signo].callback = func;
		signals[signo].active = 1;
		signals[signo].signo = signo;
		swSignal_set(signo, swSignal_async_handler, 1, 0);
	}
}

static void swSignalfd_clear()
{
    if (signal_fd)
    {
        if (sigprocmask(SIG_UNBLOCK, &signalfd_mask, NULL) < 0)
        {
            swSysError("sigprocmask(SIG_UNBLOCK) failed.");
        }
        close(signal_fd);
        bzero(&signalfd_mask, sizeof(signalfd_mask));
    }
    signal_fd = 0;
}

void swSignal_clear(void){
	if (SwooleG.use_signalfd)
    {
        swSignalfd_clear();
    }
}

/**
 * clear all singal
 */
void swSignal_none(void)
{
    sigset_t mask;
    sigfillset(&mask);
    int ret = pthread_sigmask(SIG_BLOCK, &mask, NULL);
    if (ret < 0)
    {
        swWarn("pthread_sigmask() failed. Error: %s[%d]", strerror(ret), ret);
    }
}
